package by.koleda.photoupdater.helpers;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;

import android.content.ContentUris;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.provider.ContactsContract;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;
import by.koleda.photoupdater.R;

public class ImageLoader {
	
	public static final String TAG = ImageLoader.class.getSimpleName();
	
	public static final String CONTACT_TAG = "contactId = ";
	
	public static final String IMAGE_LOADER_SERVICE = "by.koleda.android.imageloader";
	
	public static ImageLoader get(Context context) {
		ImageLoader imageLoader = (ImageLoader) context.getSystemService(IMAGE_LOADER_SERVICE);
		if (imageLoader == null) {
			context = context.getApplicationContext();
			imageLoader = (ImageLoader) context.getSystemService(IMAGE_LOADER_SERVICE);
		}
		if (imageLoader == null) {
			Log.e(TAG, "Error during call of ImageLoader");
			throw new IllegalStateException("Error during call of ImageLoader");
		}
		return imageLoader;
	}
		
	private LruCache<String, Bitmap> mMemoryCache;
	
	private FileCache fileCache;
	
	private Context mContext;
	
	private Bitmap mLoadingBitmap;
	
	private int mStub = R.drawable.ic_contact_silhouette;
	
	public ImageLoader(Context context) {
	
		final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
		
		final int cacheSize = maxMemory / 8;
		
		mContext = context;
		
		mMemoryCache = new LruCache<String, Bitmap>(cacheSize);
		
		setLoadingImage(mStub);
		
		fileCache = new FileCache(context);
	
	}
	
	public void setLoadingImage(final int resId) {
		mLoadingBitmap = BitmapFactory.decodeResource(mContext.getResources(), resId);
	}
	
	public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
		if (getBitmapFromMemCache(key) == null) {
			mMemoryCache.put(key, bitmap);
		}
	}

	public Bitmap getBitmapFromMemCache(String key) {
		return mMemoryCache.get(key);
	}
	
	public void loadBitmap(ImageView imageView, String url) {
		
		final Bitmap bitmap = getBitmapFromMemCache(url);
		if (bitmap != null) {
			imageView.setImageBitmap(bitmap);
		} else {
			if (cancelPotentialWork(imageView, url)) {
			imageView.setImageResource(R.drawable.ic_contact_silhouette);
			BitmapWorkerTask task = new BitmapWorkerTask(imageView);
			final AsyncDrawable asyncDrawable = new AsyncDrawable(mContext.getResources(), mLoadingBitmap, task);
			imageView.setImageDrawable(asyncDrawable);
			task.execute(url);
			}
		}
	}
	
	public void loadContactBitmap(ImageView imageView, String url) {
		url = "contactId = " + url;
		final Bitmap bitmap = getBitmapFromMemCache(url);
		if (bitmap!= null) {
			imageView.setImageBitmap(bitmap);
		} else {
			if (cancelPotentialWork(imageView, url)) {
			imageView.setImageResource(R.drawable.ic_contact_silhouette);
			BitmapWorkerTask task = new BitmapWorkerTask(imageView);
			final AsyncDrawable asyncDrawable = new AsyncDrawable(mContext.getResources(), mLoadingBitmap, task);
			imageView.setImageDrawable(asyncDrawable);
			task.execute(url);
			}
		}
	}
	
	public static boolean cancelPotentialWork(ImageView imageView, String data) {
		final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

		if (bitmapWorkerTask != null) {
			final String bitmapData = bitmapWorkerTask.url;
			if (bitmapData != data) {
				bitmapWorkerTask.cancel(true);
			} else {
				return false;
			}
		}
		return true;
	}
	
	private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
		if (imageView != null) {
			final Drawable drawable = imageView.getDrawable();
			if (drawable instanceof AsyncDrawable) {
				final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
				return asyncDrawable.getBitmapWorkerTask();
			}
		}
		return null;
	}
	
	
	private static class AsyncDrawable extends BitmapDrawable {
		private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
		
		public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
			super(res, bitmap);
			bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
		}
		
		public BitmapWorkerTask getBitmapWorkerTask() {
			return bitmapWorkerTaskReference.get();
		}
	}
	
	
	private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
		private final WeakReference<ImageView> imageViewReference;
		private String url;
		
		public BitmapWorkerTask(ImageView imageView) {
			// Use a WeakReference to ensure the ImageView can be garbage collected
			imageViewReference = new WeakReference<ImageView>(imageView);
		}

		@Override
		protected Bitmap doInBackground(String... params) {
			url = params[0];
			File file = fileCache.getFile(url);
			Bitmap bitmap = null;
			bitmap = decodeFile(file);
			if (bitmap == null) {
				if (url.startsWith(CONTACT_TAG)) {
					bitmap = getContactBitmap(url, file);
				} else {
					bitmap = getBitmap(url, file);
				}
			}
			if (bitmap != null) {
				addBitmapToMemoryCache(url, bitmap);
			}
			return bitmap;
		}

		@Override
		protected void onPostExecute(Bitmap bitmap) {
			if (isCancelled()) {
				bitmap = null;
			}
			
			if (imageViewReference != null && bitmap != null) {
				final ImageView imageView = imageViewReference.get();
				final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
				if (this == bitmapWorkerTask && imageView != null) {
					imageView.setImageBitmap(bitmap);
				}
			}
		
		}
		
	}
	
	public Bitmap getBitmap(String url, File file) {
		Bitmap bitmap = null;
		if (file == null) {
			file = fileCache.getFile(url);
			bitmap = decodeFile(file);
			if (bitmap != null) {
				return bitmap;
			}
		}
		try {
			URL imageUrl = new URL(url);
			HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
			conn.setConnectTimeout(30000);
			conn.setReadTimeout(30000);
			conn.setInstanceFollowRedirects(true);
			InputStream is = conn.getInputStream();
			OutputStream os = new FileOutputStream(file);
			Utils.copyStream(is, os);
			os.close();
			if (is != null) {
				is.close();
			}
			bitmap = decodeFile(file);
			return bitmap;
		} catch	(Exception e) {
			Log.w(TAG, e.getMessage());
			return null;
		}
	}
	
	private Bitmap getContactBitmap(String id, File file) {
		Long i = Long.parseLong(id.substring(CONTACT_TAG.length()));
		Bitmap bitmap = null;
		InputStream inputStream = null;
		try {
			inputStream = ContactsContract.Contacts.openContactPhotoInputStream(mContext.getContentResolver(),
            		ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, i));
			if (inputStream != null) {
				bitmap = BitmapFactory.decodeStream(inputStream);
			}
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					Log.w(TAG, e.getMessage());
				}
			}
		}
		return bitmap;
	}
	
	private Bitmap decodeFile(File file) {
		try {
			return BitmapFactory.decodeStream(new FileInputStream(file));
		} catch (FileNotFoundException e) {
			return null;
		}
	}
	

}
